<template>
  <div>
    <div class="table" v-if="type === 'table'">
      <el-table
        :data="fileList"
        border
        style="width: 100%">
        <el-table-column
          label="上传列表"
          align="center"
        >
          <el-table-column
            prop="url"
            label="预览">
            <template slot-scope="scope">
              <img :src="scope.row.url" alt="" style="width: 80px;height: 80px;">
            </template>
          </el-table-column>
          <el-table-column
            prop="name"
            label="文件名">
          </el-table-column>
          <el-table-column
            prop="status"
            label="上传状态">
          </el-table-column>
          <el-table-column
            prop="percentage"
            label="上传进度">
            <template slot-scope="scope">
              <el-progress
                :text-inside="true"
                :stroke-width="18"
                :percentage="Math.floor(scope.row.percentage)"
                :status="scope.row.status"
              >
              </el-progress>
            </template>
          </el-table-column>
          <!-- TODO 预留，其他列操作 -->
          <slot name="columns">
          </slot>
        </el-table-column>
      </el-table>
      <div class="btn-group">
        <el-upload
          class="upload-demo"
          ref="upload"
          :auto-upload="false"
          :show-file-list="showFileList"
          :action="action"
          :on-success="onSuccess"
          :on-progress="onProgress"
          :on-change="_uploadChange"
          :on-error="_error"
          :before-upload="uploadBefore"
          :on-remove="onRemove"
          :accept="accept"
          :limit="limit"
          :on-exceed="_exceed"
          :multiple="multiple">
          <el-button
            slot="trigger"
            size="small"
            icon="el-icon-plus"
          >选择文件
          </el-button>
          <el-button
            type="success"
            @click="submitFile"
            size="small"
            icon="el-icon-upload"
          >
            点击上传
          </el-button>
          <!-- TODO 预留，自定义按钮操作 -->
          <slot name="upload-table-button"></slot>
        </el-upload>
      </div>
    </div>

    <div class="fx" v-if="type === 'square'">
      <el-upload
        ref="square"
        :multiple="multiple"
        :action="action"
        :on-success="_success"
        :on-progress="onProgress"
        :on-change="_uploadChange"
        :before-upload="_beforeUpload"
        :on-remove="onRemove"
        :on-error="_error"
        :accept="accept"
        :show-file-list="showFileList"
        :limit="limit"
        :file-list="filterFileList"
        :on-exceed="_exceed"
        :on-preview="handlePictureCardPreview"
        :list-type="listType">
        <slot name="upload-square-button">
          <i class="el-icon-plus" v-if="listType === 'picture-card'"></i>
          <el-button v-else size="mini">点击上传</el-button>
        </slot>
      </el-upload>
      <el-dialog :visible.sync="dialog.show">
        <img style="width: 100%" :src="dialog.data.url" alt="">
      </el-dialog>
    </div>
  </div>
</template>

<script>
  const fileSuffix = {
    image: ['bmp', 'jpg', 'png', 'gif', 'pcx', 'svg', 'dxf', 'webp', 'jpeg'],
    video: ['avi', 'rmvb', 'rm', 'asf', 'divx', 'mpg', 'mpeg', 'mpe', 'mp4', 'mkv', 'vob']
  };
  export default {
    name: "index",
    components: {},
    props: {
      type: { // 上传的类型【table || square】
        default: 'square',
        type: String
      },
      listType: String, // type = table时无效
      action: {
        type: String,
        required: true
      }, // 上传文件的地址
      // uploadButton: Array, // 上传类型等于table时需要，指定上传按钮

      onSuccess: Function, // 上传成功

      onProgress: Function, // 上传文件被点击时候的事件
      onError: Function, // 文件上传失败时的钩子

      onExceed: Function, // 文件超出限制

      onChange: {
        type: Function,
      }, // 文件状态改变时的钩子
      uploadBefore: Function, // 文件上传之前的钩子
      onRemove: Function, // 文件删除的钩子
      fileList: { // 文件列表
        type: [Array, String],
        default() {
          return []
        }
      },
      limit: { // 最大上传数
        type: Number,
        default: 20
      },
      accept: { // 可上传的格式，需要所有文件都能上传时，该值留空
        type: String,
        default: 'image/*'
      },
      multiple: { // 是否可以多选
        type: Boolean,
        default: true
      },
      showFileList: {
        type: Boolean,
        default: true
      }, // 是否显示上传列表

      urlField: { // 图片对象的url字段， TODO 单词拼错了
        type: String,
        default: 'url'
      },
      max: { // 文件上传最大的内存
        type: Number,
        default: 50
      },
      clearUploadingFile: { // 是否清除已经上传成功的列表文件
        type: Boolean,
        default: false
      },
      split: String, // 分隔符，当fileList为字符串时，需要指定分隔符
    },
    data() {
      return {
        percentage: 0, // 文件上传的进度
        dialog: {
          show: false,
          data: ''
        },
        suffix: {}, // 可上传的后缀对象
      }
    },
    computed: {
      filterFileList() {
        let filterArr = [];
        if (!this.fileList) return filterArr;
        if (Array.isArray(this.fileList)) {
          this.fileList.forEach((item, index) => {
            // name 和 url 是必须得
            if (typeof item === 'string') {
              filterArr.push({
                name: String(index),
                url: item
              });
            } else if (typeof item === 'object') {
              if (!(item['url'] && this.urlFiled)) throw new Error('图片列表的显示格式不合法');
              // url 由用户指定
              filterArr.push({
                name: item.hasOwnProperty('name') ? item['name'] : String(index),
                url: item[this.urlFiled]
              });
            }
          });
        } else if (typeof this.fileList === 'string') {
          filterArr = this.split ? this.fileList.split(this.split) : [this.fileList]
        }
        return filterArr;
      }
    },
    created() {
      this.suffix = this._generateFileSuffix(this.accept);
    },
    mounted() {
    },
    methods: {
      /**
       * @description 生成可上传的文件后缀对象
       * @param rule {string} 可上传的文件后缀
       * @param split {string} 规则分割符，默认“,”
       */
      _generateFileSuffix(rule, split = ',') {
        if (split.trim() === '/') throw new Error('分隔符不能使用 / ！');
        let _suffix = {};
        if (rule) {
          // 得到用户指定的上传类型
          let acceptSplit = this.accept.split(split);
          // 生成可上传的文件后缀对象
          acceptSplit.forEach(item => {
            let splitItem = item.split('/');

            if (_suffix[splitItem[0]]) {
              if (_suffix[splitItem[0]].indexOf(splitItem[1]) === -1) {
                _suffix[splitItem[0]].push(splitItem[1]);
              }
            } else {
              _suffix[splitItem[0]] = [];
              _suffix[splitItem[0]].push(splitItem[1]);
            }
          });
        }
        return _suffix;
      },
      /**
       * @description 上传成功方法，判断是否已经全部上传完毕
       * @param res
       * @param file
       */
      _success(res, file) {
        let files = this.$refs.square.uploadFiles;
        if (files && files.length) {
          let index = files.findIndex(item => item.status !== 'success');
          this.onSuccess(res, file, index === -1); // 最后一个：是否全部上传成功
        }
      },
      /**
       * @description 文件上传失败
       * @param err 错误消息
       * @param file 上传失败的文件
       * @param fileList 文件列表
       */
      _error(err, file, fileList) {
        if (this.onError) {
          this.onError(err, file, fileList)
        } else {
          console.error(err);
          this.$message.error(err.message);
        }
      },
      submitFile() {
        this.$refs['upload'].submit();
      },
      /**
       * @description 文件上传之前的事件
       * @param file 上传的文件
       * @private
       */
      _beforeUpload(file) {
        if (this.uploadBefore) {
          // 用户自定义了上传方法
          this.uploadBefore(file)
        } else {
          // 默认上传方法
          if (Object.keys(this.suffix).length) {
            // 得到用户正在上传的文件名
            const uploadingFileType = file.type.split('/');
            if (!(this.suffix[uploadingFileType[0]]
              &&
              (
                this.suffix[uploadingFileType[0]].indexOf('*') !== -1 || this.suffix[uploadingFileType[0]].indexOf(uploadingFileType[1]) !== -1
              )
            )) {
              // 不存在
              this.$message.error('文件类型上传错误');
              return false;
            }
            // 合法文件
            const hasFile = file.size / 1024 / 1024 > this.max;
            if (hasFile) {
              this.$message.error(`文件大小不能超过 ${this.max} MB！`);
              return false;
            }
            return true;
          } else return true // 不指定，任何文件都能上传
        }
      },
      uploadProgress(event, file, fileList) {
        console.group('文件上传时的钩子')
        console.log('event：', event)
        console.log('file：', file)
        console.log('进度：', JSON.stringify(file.percentage))
        console.log('状态：', JSON.stringify(file.status))
        console.log('fileList：', fileList)
        console.groupEnd()
      },
      _uploadChange(file, fileList) {
        if (this.onChange) {
          this.onChange(file, fileList);
        } else {
          if (this.clearUploadingFile) {
            let rem = JSON.parse(JSON.stringify(fileList))
              , remSum = 0 // 需要删除的索引
            ;
            rem.forEach((item, index) => {
              item.status === 'success' && fileList.splice(index - remSum, 1);
            })
          } else if (this.type === 'table') {
            this.fileList = fileList;
          }
        }
      },
      /**
       * @description 上传超过限制
       * @private
       */
      _exceed(files, fileList) {
        if (this.onExceed) {
          this.onExceed(files, fileList);
        } else {
          this.$message.error('老大说了，只能让您上传六个文件~')
          return false;
        }
      },
      uploadSuccess(response, file, fileList) {
        console.group('上传成功')
        console.log('response：', response)
        console.log('file：', file)
        console.log('进度：', JSON.stringify(file.percentage))
        console.log('状态：', JSON.stringify(file.status))
        console.log('fileList：', fileList)
        console.groupEnd()
      },
      remove(file, fileList) {
        console.group('文件删除')
        console.log('file：', file)
        console.log('进度：', JSON.stringify(file.percentage))
        console.log('状态：', JSON.stringify(file.status))
        console.log('fileList：', fileList)
        console.groupEnd()
      },
      handlePictureCardPreview(file) {
        this.dialog.data = file;
        this.dialog.show = true;
      }
    },
  }
</script>

<style lang="scss" scoped>
  .btn-group {
    margin-top: 20px;
  }
</style>
